﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Navigation;
using System.Configuration;
using System.Text;
using System.IO;
using Microsoft.Office.Server.WebControls;
using Microsoft.Office.DocumentManagement;
//using Microsoft.Office.DocumentManagement.MetadataNavigation;

namespace ExtendedTreeView.ExtendedTreeView
{
    [ToolboxItemAttribute(false)]
    public class ExtendedTreeView : System.Web.UI.WebControls.WebParts.WebPart
    {
        #region Global Variables
        private IHierarchicalEnumerable _dataItems;
        //field to ignore, almost all of these are the built in fields with the exception of "AllDocs" and "originalItemID"
        public List<string> ignoredFields = new List<string>() 
        { "Name", "Attachments", "DocIcon", "Title", "ID", "Created By", "Modified By", "Edit", "originalItemID", "AllDocs", "Created", "Modified", "Classification Date", "Type",
        "Content Type ID","Approver Comments","Document Modified By","Document Created By","File Type","HTML File Type","Source URL","Shared File Index","Template Link","HTML File Link","Is Signed","Notes","Document ID Value","Document ID","Persist ID","Content Type","Has Copy Destinations","Copy Source","Approval Status","URL Path","Path","File Size","Item Type",
"Sort Type","Effective Permissions Mask","ID of the User who has the item Checked Out",
"Is Checked out to local","Checked Out To","Unique Id","Client Id","Virus Status","Checked Out To","Check In Comment",
"Checked Out To","Edit Menu Table Start","Edit Menu Table Start","Edit Menu Table End","Server Relative URL","Encoded Absolute URL","File Size","Property Bag","Level","Is Current Version",
"Item Child Count","Folder Child Count","Select","UI Version","Version","Instance ID","Workflow Version","Workflow Instance ID",
"Source Version (Converted Document)","Source Name (Converted Document)","Document Concurrency Number","Merge","Relink"};
        private SPNavigationManager navMan = new SPNavigationManager();
        public TreeNodeCollection treeNodes = new TreeNodeCollection();
        public List<SPField> fieldsToScan = new List<SPField>();
        #endregion

        #region create web part
        protected override void CreateChildControls()
        {
            base.CreateChildControls();
            //Controls.Add(new LiteralControl("<link href=\"http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css\" rel=\"stylesheet\" type=\"text/css\"/><script src=\"http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js\"></script>  <script src=\"http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js\"></script>  <style type=\"text/css\">    #resizeNavigation { width: 150px; position:relative; z-index:200; height: 100%; background: silver; }  </style>  <script>window.onload = (function(){try{  $(document).ready(function() {    $(\"#resizeNavigation\").resizable();  });  }catch(e){}});</script>"));
            Controls.Add(new LiteralControl("<style type=\"text/css\">.expandNav {width:300px !important; z-index:400px;}.smallNav {width:150px !important;}</style><script type=\"text/Javascript\">function expandNav(id) {var whichpost = document.getElementById(id);if (whichpost.className==\"expandNav\") { whichpost.className=\"smallNav\"; } else { whichpost.className=\"expandNav\"; }} </script>"));
            Controls.Add(new LiteralControl("<a href=\"javascript:expandNav('resizeNavigation')\">Resize Navigation</a>"));

            try
            {
                //add local quick launch links to tree
                AddQuickLaunchLinks(SPContext.Current.Web.Navigation.QuickLaunch);
                //go grab and convert the built in tree view data source
                InitDataSourceControl();
                //check for user specified fields to ignore
                //ParseIgnoredFieldsFile();
                IHierarchyData node;
                foreach (object dataItem in _dataItems)
                {
                    node = _dataItems.GetHierarchyData(dataItem);
                    //add each node to collection after conversion
                    treeNodes.Add(ConvertNode(node));
                }
                //make sure we have data ;)
                if (treeNodes != null)
                {
                    //Build Tree View
                    SPTreeView spTreeView = new SPTreeView();
                    spTreeView.ID = "WebTreeView";
                    spTreeView.ShowLines = true;
                    spTreeView.ExpandDepth = 0;
                    spTreeView.EnableClientScript = true;
                    spTreeView.EnableViewState = false;
                    spTreeView.NodeStyle.CssClass = "ms-navitem";
                    spTreeView.NodeStyle.HorizontalPadding = 2;
                    spTreeView.SelectedNodeStyle.CssClass = "ms-tvselected";
                    spTreeView.SkipLinkText = "";
                    spTreeView.NodeIndent = 12;
                    spTreeView.AutoExpandSelectedNode = true;
                    spTreeView.ShowLines = false;
                    spTreeView.ExpandImageUrl = "/_layouts/images/MDNCollapsed.png";
                    spTreeView.CollapseImageUrl = "/_layouts/images/MDNExpanded.png";
                    spTreeView.NoExpandImageUrl = "/_layouts/images/tvblank.gif";
                    SPList list = null;
                    try
                    {
                        list = SPContext.Current.List;
                    }
                    catch (Exception) { }
                    //add nodes, we have to do this because sptreeview won't bind to any data source that is easy to read and write to
                    foreach (TreeNode tNode in treeNodes)
                    {
                        if (tNode.Value == "ListNode")
                        {
                            try
                            {
                                if (list != null && list.Title == tNode.Text)
                                {
                                    tNode.Expand();
                                    TreeNode extendedNode = null;
                                    try
                                    {
                                        //attempt to get and add all special fields from the current list and add them to the tree view
                                        extendedNode = SetFields(list.DefaultView.ViewFields, tNode, list);
                                    }
                                    catch (Exception ex)
                                    {
                                        string exception = ex.Message;
                                    }
                                    finally
                                    {
                                        string parseUrl = Context.Request.RawUrl;
                                        string host = Context.Request.Url.Host;
                                        if (extendedNode != null)
                                        {
                                            spTreeView.Nodes.Add(extendedNode);
                                            spTreeView.Nodes[spTreeView.Nodes.IndexOf(extendedNode)].Expand();
                                            if(extendedNode.ChildNodes.Count >= 0) 
                                            {
                                                foreach (TreeNode child in extendedNode.ChildNodes)
                                                {
                                                    if (child.NavigateUrl == Context.Request.RawUrl)
                                                    {
                                                        spTreeView.Nodes[spTreeView.Nodes.IndexOf(child)].Expand();
                                                    }
                                                }
                                            }
                                        }
                                        else
                                        {
                                            spTreeView.Nodes.Add(tNode);
                                            spTreeView.Nodes[spTreeView.Nodes.IndexOf(tNode)].Expand();
                                            if (tNode.ChildNodes.Count >= 0)
                                            {
                                                foreach (TreeNode child in tNode.ChildNodes)
                                                {
                                                    if (child.NavigateUrl == Context.Request.RawUrl)
                                                    {
                                                        spTreeView.Nodes[spTreeView.Nodes.IndexOf(child)].Expand();
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    //if we are not on a list or if the node is for a different list, ignore it
                                    spTreeView.Nodes.Add(tNode);
                                }
                            }
                            catch (Exception ex)
                            {
                                string exception = ex.Message;
                            }
                        }
                        else
                        {
                            spTreeView.Nodes.Add(tNode);
                        }
                    }
                    //build navigation resize handle
                    //NavResizerControl navResizer = new NavResizerControl();
                    //navResizer.ID = "NavigationResizer";
                    //navResizer.ResizeTargetControlName = spTreeView.ID;
                    //navResizer.Width = new Unit(10);
                    //add the navigation state manager for hosting the treeview
                    navMan.ID = "TreeViewNavigationManager";
                    navMan.ContainedControl = "TreeView";
                    navMan.EnableViewState = true;
                    //add the treeview to the manager
                    navMan.Controls.Add(spTreeView);
                    //add resize handle
                    //navMan.Controls.Add(navResizer);
                    //add everything to the page :)
                    Controls.Add(navMan);
                }
            }
            catch (Exception ex)
            {
                //Controls.Add(new LiteralControl(ex.Message));
            }
        }
        #endregion

        //parse the ignored fields file
        public void ParseIgnoredFieldsFile()
        {
            try
            {
                string readFields = File.ReadAllText("/_layouts/ExtendedTreeView/IgnoredFields.txt");
                //treat as csv
                string[] fields = readFields.Split(',');
                foreach (string field in fields)
                {
                    string f = field;
                    //make sure we have no trailing white space around the field name
                    try
                    {
                        f = f.TrimStart(' ');
                    }
                    catch (Exception) { }
                    try
                    {
                        f = f.TrimEnd(' ');
                    }
                    catch (Exception) { }
                    ignoredFields.Add(f);
                } 
            }
            catch (Exception ex)
            {
                //Controls.Add(new LiteralControl(ex.Message + "</br>"));
            }
        }

        #region figure out fields to show and add them to tree view
        //handle the population of the custom nodes in to the tree view at run time
        public TreeNode SetFields(SPViewFieldCollection viewFields, TreeNode node, SPList list)
        {
            if (node.ChildNodes.Count >= 1)
            {
                try
                {
                    //try and get number of items in each folder within the list
                    node = ScanFolders(node);
                }
                catch (Exception) { }
            }
            /*string header = list.DefaultView.GroupByHeader.ToString();
            string footer = list.DefaultView.GroupByFooter.ToString();
            Controls.Add(new LiteralControl(header + "</br>"+ footer + "</br>"));*/
            SPFieldCollection fields = list.Fields;
            fieldsToScan.Clear();
            string[] splitter1 = new string[] { "</GroupBy>" };
            string[] splitter2 = new string[] { "Name=" };
            string xml = list.DefaultView.SchemaXml.ToString();
            //get grouped field names from default view
            try
            {
                if (xml.Contains("GroupBy"))
                {
                    string groupField = "";
                    string[] groups = xml.Split(splitter1, 5, StringSplitOptions.None);
                    string[] groupedFields;
                    foreach (string group in groups)
                    {
                        if (group.Contains("GroupBy"))
                        {
                            groupedFields = group.Split(splitter2, 5, StringSplitOptions.None);
                            for (int i = 3; i < groupedFields.Length; i++)
                            {
                                if (i < 5)
                                {
                                    groupField = groupedFields[i];
                                    if (groupField.Contains('"'))
                                    {
                                        groupField = groupField.Remove(0, 1);
                                        groupField = groupField.Remove(groupField.IndexOf('"'));
                                        SPField groupedField = fields.GetFieldByInternalName(groupField);
                                        fieldsToScan.Add(groupedField);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                string msg = ex.Message;
            }
            SPField field;
            System.Collections.Specialized.StringCollection stringCol =  viewFields.ToStringCollection();
            bool includeField = true;
            foreach (String fieldName in stringCol)
            {
                field = fields.GetFieldByInternalName(fieldName);
                foreach (string ignoredField in ignoredFields)
                {
                    if (field.Title.ToUpperInvariant() == ignoredField.ToUpperInvariant())
                    {
                        includeField = false;
                    }
                }
                if (includeField == true)
                {
                    fieldsToScan.Add(field);
                }
            }
            try
            {
                node = GetMetadataNodes(node, list, fieldsToScan);
            }
            catch (Exception ex)
            {
                string exMsg = ex.Message;
            }
            #region legacy test code that lists all columns within the current library
            /*foreach (SPField field in fields)
            {
                if (field.Hidden == true)// | field.Type != SPFieldType.Text | field.Type != SPFieldType.Choice | field.Type != SPFieldType.Number)
                {
                    //ignore hidden fields
                    //ignore non-text fields
                    //ignore non-number fields
                    //ignore non-choice fields
                }
                else
                {
                    bool includeField = true;
                    //check if this a field to ignore
                    foreach (string ignoredField in ignoredFields)
                    {
                        if (field.Title.ToUpperInvariant() == ignoredField.ToUpperInvariant())
                        {
                            includeField = false;
                        }
                    }
                    if (includeField == true)
                    {
                        try
                        {
                            fieldsToScan.Add(field);
                        }
                        catch (Exception ex)
                        {
                            string exm = ex.Message;
                        }
                    }
                }
            }
            try 
            {
                node = GetMetadataNodes(node, list, fieldsToScan);
            }
            catch(Exception) {}
             * */
            #endregion
            return node;
        }

        //get files per folder
        public TreeNode ScanFolders(TreeNode node)
        {
            try
            {
                SPFolder folder;
                TreeNode child;
                string rawUrl = Context.Request.RawUrl;
                rawUrl = rawUrl.Replace(@"%2f", @"/");
                rawUrl = rawUrl.Replace(@"%20", " ");
                for(int i = 0; i < node.ChildNodes.Count; i++)
                {
                    try
                    {
                        child = node.ChildNodes[i];
                        //get the spfolder for the specified tree view node
                        folder = SPContext.Current.Web.GetFolder(child.NavigateUrl);
                        //check if the user is currently in this folder
                        if (rawUrl.Contains(child.NavigateUrl) == true)
                        {
                            child.ImageUrl = "/_layouts/images/openfold.gif";
                            child.Expand();
                        }
                        //get count of items under specified folder
                        int count = folder.ItemCount;
                        //add the count of items to the folders text and tooltip :)
                        child.ToolTip = child.ToolTip + " [" + count.ToString() + "]";
                        child.Text = child.ToolTip;
                        if (child.ChildNodes.Count >= 1)
                        {
                            try
                            {
                                //recursively scan through the childrens children
                                child = ScanFolders(child);
                            }
                            catch (Exception) { }
                        }
                        //replace original child with updated child.
                        node.ChildNodes.AddAt(i, child);
                    }
                    catch (Exception ex)
                    {
                       //Controls.Add(new LiteralControl(ex.Message + "</br>"));
                    }
                }
            }
            catch (Exception ex)
            {
                //Controls.Add(new LiteralControl(ex.Message));
            }
            return node;
        }

        //get metadata nodes for all fields at once
        public TreeNode GetMetadataNodes(TreeNode node, SPList list, List<SPField> fields)
        {
            SPQuery query = new SPQuery();
            TreeNode child = null;
            TreeNode gchild;
            DataTable tempTbl;
            DataView dv;
            DataTable dt = null;
            DataRow[] rows;
            SPListItemCollection itemCollection;
            bool failed = false;
            string fieldParse = "";
            string parseFieldName = "";
            try
            {
                StringBuilder fieldBuilder = new StringBuilder();
                foreach (SPField field in fields)
                {
                    fieldBuilder.Append(field.InternalName + ",");
                }
                fieldParse = fieldBuilder.ToString();
                fieldParse = fieldParse.Remove(fieldParse.Length - 1);
                string[] allFields = fieldParse.Split(',');
                //query for the values of that field
                //query for non-null values of the specified field within the list
                //query.Query = "<OrderBy><FieldRef Name='" + field.Title.ToString() + "' /></OrderBy><Where><IsNotNull><FieldRef Name='" + field.Title.ToString() + "' /></IsNotNull></Where>";
                //query.ViewAttributes = "Scope=\"Recursive\"";
                if (fieldParse != "")
                {
                    itemCollection = list.GetItems(allFields);
                    //this foreach is for debugging
                    ///TODO: Replace this with column["displayname"]
                    if(itemCollection.Count >= 1) 
                    {
                        //TODO: add in handling for multi-value columns
                        foreach (SPField field in fields)
                        {
                            if (dt != null)
                            {
                                dt = null;
                            }
                            //build child node
                            child = new TreeNode(field.Title, "MetadataField", "/_layouts/images/mdnfieldlabel.png");
                            child.NavigateUrl = node.NavigateUrl;
                            child.Target = "_self";
                            dt = new DataTable("Table");
                            dt.Columns.Add("Value");
                            dt.Columns.Add("URL");
                            dt.Columns.Add("Type");
                            dt.Columns.Add("Target");
                            dt.Columns.Add("ImageUrl");
                            string value = "";
                            foreach (SPListItem item in itemCollection)
                            {
                                
                                try 
                                {
                                    value = item[field.Id].ToString();
                                }
                                catch(Exception) {}
                                if(value != "") 
                                {
                                    //metadata tag image
                                    //_layouts/images/mdnvalue.png
                                    //URL format
                                    //&VisibilityContext=WSSTabPersistence#ServerFilter=FilterField1=Litigation%5Fx0020%5FDiligence-FilterValue1=
                                    //Litigation-TreeField=Litigation%5Fx0020%5FDiligence-TreeValue=Litigation-OverrideScope=RecursiveAll-ProcessQStringToCAML=1
                                    StringBuilder sb = new StringBuilder();
                                    sb.Append(list.DefaultViewUrl);
                                    //get root of current list
                                    string parseFolder = "RootFolder=" + list.RootFolder.ServerRelativeUrl;
                                    //if user is in a sub-folder figure out the folder and add that path to the url
                                    try
                                    {
                                        string parseUrl = Context.Request.RawUrl;
                                        parseUrl = parseUrl.Remove(0, parseUrl.IndexOf("RootFolder"));
                                        if (parseUrl != "")
                                        {
                                            parseFolder = parseUrl;
                                        }
                                    }
                                    catch (Exception)
                                    {

                                    }
                                    sb.Append("?" + parseFolder);
                                    parseFieldName = field.InternalName;
                                    parseFieldName = parseFieldName.Replace(" ", "%5F");
                                    value = value.Replace(" ", "%20");
                                    value = value.Replace("-", "%2D");
                                    sb.Append("&VisibilityContext=WSSTabPersistence#ServerFilter=FilterField1=" + parseFieldName);
                                    sb.Append("&FilterValue1=" + value + "&TreeField=" + parseFieldName + "&TreeView=" + field.Title);
                                    sb.Append("&OverrideScope=RecursiveAll&ProcessQStringToCAML=1");
                                    value = value.Replace("%20", " ");
                                    value = value.Replace("%2D", "-");

                                    //gchild = new TreeNode(value, "MetadataTag", "/_layouts/images/mdnvalue.png", sb.ToString(), "_self");
                                    object[] rowValues = new object[5] { value, sb.ToString(), "MetadataTag", "_self", "/_layouts/images/mdnvalue.png" };
                                    dt.Rows.Add(rowValues);
                                }
                            }
                            dv = new DataView(dt);
                            string[] columnNames = new string[5] {"Value", "URL", "Type", "Target", "ImageUrl"};
                            tempTbl = dv.ToTable("Table", true, columnNames);
                            dv = new DataView(tempTbl);
                            dv.Sort = "Value ASC";
                            tempTbl = dv.ToTable();
                            
                            if (tempTbl.Rows.Count >= 1)
                            {
                                foreach (DataRow row in tempTbl.Rows)
                                {
                                    //get count of the number of times the current value appears within the library
                                    rows = dt.Select("Value = '" + row["Value"].ToString() + "'");
                                    //show user NodeValue + [CountOfValue]
                                    gchild = new TreeNode(row["Value"].ToString() + " [" + rows.Length.ToString() + "]", row["Type"].ToString(), row["ImageUrl"].ToString(), row["URL"].ToString(), row["Target"].ToString());
                                    gchild.ToolTip = row["Value"].ToString() + " [" + rows.Length.ToString() + "]";
                                    if (Context.Request.RawUrl.Contains(parseFieldName) == true)
                                    {
                                        child.Expand();
                                        value = value.Replace(" ", "%20");
                                        value = value.Replace("-", "%2D");
                                        if (Context.Request.RawUrl.Contains(value) == true)
                                        {
                                            gchild.ImageUrl = "/_layouts/Images/MDNValueSelected.png";
                                            gchild.Expand();
                                        }
                                    }
                                    child.ChildNodes.Add(gchild);
                                }
                                if (child.ChildNodes.Count <= 0)
                                {
                                    //ignore metadata fields with no selectable data
                                }
                                else
                                {
                                    //add count of nodes to each metadata heading
                                    child.Text = child.Text;
                                    child.ToolTip = child.Text;
                                    node.ChildNodes.Add(child);
                                }
                            }
                            }
                        }
                    }
                }
            catch (Exception ex)
            {
                failed = true;
                //Controls.Add(new LiteralControl(ex.Message + "</br>"));
            }
            if (failed == true)
            {
                try
                {
                    //node = GetMetadataValuesOnFailure(list, node, field);
                }
                catch (Exception) { }
            }
            return node;
        }

        #region Depreciated
        //if the datatable failed to pull data properly requery list and manually iterate through collection. Not as fast or pretty but it works.
        /// <summary>
        /// Depreciated in favor of alternate method for getting all field values
        /// </summary>
        /// <param name="list"></param>
        /// <param name="node"></param>
        /// <param name="field"></param>
        /// <returns></returns>
        private TreeNode GetMetadataValuesOnFailure(SPList list, TreeNode node, SPField field)
        {
            TreeNode child;
            TreeNode gchild;
            try
            {
                List<string> allValues = new List<string>();
                //query.Query = "<Query></Query>";
                //query.ViewAttributes = "Scope=\"Recursive\"";
                SPListItemCollection items = list.Items;
                if (items.Count >= 0)
                {
                        child = new TreeNode(field.Title, "MetadataField", "/_layouts/images/mdnfieldlabel.png");
                        child.NavigateUrl = node.NavigateUrl;
                        child.Target = "_self";
                        foreach (SPListItem item in items)
                        {
                            string value = "";
                            try
                            {
                                value = item[field.Title].ToString();
                            }
                            catch (Exception) { }
                            if (value != "")
                            {
                                allValues.Add(value);
                            }
                        }
                        allValues = allValues.Distinct().ToList();
                        if (allValues.Count >= 0)
                        {
                            foreach (string value in allValues)
                            {
                                //metadata tag image
                                //_layouts/images/mdnvalue.png
                                //URL format
                                //&VisibilityContext=WSSTabPersistence#ServerFilter=FilterField1=Litigation%5Fx0020%5FDiligence-FilterValue1=
                                //Litigation-TreeField=Litigation%5Fx0020%5FDiligence-TreeValue=Litigation-OverrideScope=RecursiveAll-ProcessQStringToCAML=1
                                StringBuilder sb = new StringBuilder();
                                sb.Append(list.DefaultViewUrl);
                                //get root of current list
                                string parseFolder = "RootFolder=" + list.RootFolder.ServerRelativeUrl;
                                //if user is in a sub-folder figure out the folder and add that path to the url
                                try
                                {
                                    string parseUrl = Context.Request.RawUrl;
                                    parseUrl = parseUrl.Remove(0, parseUrl.IndexOf("RootFolder"));
                                    if (parseUrl != "")
                                    {
                                        parseFolder = parseUrl;
                                    }
                                }
                                catch (Exception) { }
                                //do funky stuff with characters to make internet explorer happy
                                parseFolder.Replace(@"/", @"%2F");
                                sb.Append("?" + parseFolder);
                                string parseFieldName = field.InternalName;
                                parseFieldName.Replace(" ", "%5F");
                                sb.Append("&VisibilityContext=WSSTabPersistence#ServerFilter=FilterField1=" + parseFieldName);
                                sb.Append("-FilterValue1=" + value + "-TreeField=" + parseFieldName + "-TreeView=" + field.Title);
                                sb.Append("-OverrideScope=RecursiveAll-ProcessQStringToCAML=1");

                                gchild = new TreeNode(value, "MetadataTag", "/_layouts/images/mdnvalue.png", sb.ToString(), "_self");
                                child.ChildNodes.Add(gchild);
                            }
                            if (child.ChildNodes.Count <= 0)
                            {
                                //ignore metadata fields with no selectable data
                            }
                            else
                            {
                                node.ChildNodes.Add(child);
                            }
                    }
                }
            }
            catch (Exception ex)
            {
                //Controls.Add(new LiteralControl(ex.Message + "</br>"));
            }
            return node;
        }

        ///Depreciated to save on overhead all fields are set in to an array now and all items 
        ///in the library get scanned through once with only the specified fields pulled down
        //get metadata nodes for a single field
        public TreeNode GetMetadataNodes(TreeNode node, SPList list, SPField field)
        {
            SPQuery query = new SPQuery();
            TreeNode child = null;
            TreeNode gchild;
            DataTable tempTbl;
            DataView dv;
            DataTable dt = null;
            bool failed = false;
            try
            {
                //query for the values of that field
                //query for non-null values of the specified field within the list
                query.Query = "<OrderBy><FieldRef Name='" + field.Title.ToString() + "' /></OrderBy><Where><IsNotNull><FieldRef Name='" + field.Title.ToString() + "' /></IsNotNull></Where>";
                query.ViewAttributes = "Scope=\"Recursive\"";
                tempTbl = list.GetItems(query).GetDataTable();
                dv = new DataView(tempTbl);
                bool foundMatch = false;
                //this foreach is for debugging
                ///TODO: Replace this with column["displayname"]
                foreach (DataColumn col in tempTbl.Columns)
                {
                    //Controls.Add(new LiteralControl(col.ColumnName + " : " + field.InternalName + " : " + field.Title + "</br>"));
                    if (col.Caption == field.InternalName | col.Caption == field.Title)
                    {
                        string[] colName = new string[] { col.ColumnName.ToString() };
                        foundMatch = true;
                        //copy the matching column to the new table
                        dt = dv.ToTable("DistinctTable", true, colName);
                    }
                }
                if (foundMatch == true && dt != null)
                {
                    child = new TreeNode(field.Title, "MetadataField", "/_layouts/images/mdnfieldlabel.png");
                    child.NavigateUrl = node.NavigateUrl;
                    child.Target = "_self";

                    foreach (DataRow row in dt.Rows)
                    {
                        //metadata tag image
                        //_layouts/images/mdnvalue.png
                        //URL format
                        //&VisibilityContext=WSSTabPersistence#ServerFilter=FilterField1=Litigation%5Fx0020%5FDiligence-FilterValue1=
                        //Litigation-TreeField=Litigation%5Fx0020%5FDiligence-TreeValue=Litigation-OverrideScope=RecursiveAll-ProcessQStringToCAML=1
                        StringBuilder sb = new StringBuilder();
                        sb.Append(list.DefaultViewUrl);
                        //get root of current list
                        string parseFolder = "RootFolder=" + list.RootFolder.ServerRelativeUrl;
                        //if user is in a sub-folder figure out the folder and add that path to the url
                        try
                        {
                            string parseUrl = Context.Request.RawUrl;
                            parseUrl = parseUrl.Remove(0, parseUrl.IndexOf("RootFolder"));
                            if (parseUrl != "")
                            {
                                parseFolder = parseUrl;
                            }
                        }
                        catch (Exception)
                        {

                        }
                        parseFolder.Replace(@"/", @"%2F");
                        sb.Append("?" + parseFolder);
                        string parseFieldName = field.InternalName;
                        parseFieldName.Replace(" ", "%5F");
                        sb.Append("&VisibilityContext=WSSTabPersistence#ServerFilter=FilterField1=" + parseFieldName);
                        sb.Append("-FilterValue1=" + row[field.Title].ToString() + "-TreeField=" + parseFieldName + "-TreeView=" + field.Title);
                        sb.Append("-OverrideScope=RecursiveAll-ProcessQStringToCAML=1");

                        gchild = new TreeNode(row[field.Title].ToString(), "MetadataTag", "/_layouts/images/mdnvalue.png", sb.ToString(), "_self");
                        child.ChildNodes.Add(gchild);
                    }
                    if (child.ChildNodes.Count <= 0)
                    {
                        //ignore metadata fields with no selectable data
                    }
                    else
                    {
                        node.ChildNodes.Add(child);
                    }
                }
            }
            catch (Exception)
            {
                failed = true;
            }
            if (failed == true)
            {
                try
                {
                    //node = GetMetadataValuesOnFailure(list, node, field);
                }
                catch (Exception) { }
            }
            return node;
        }
        #endregion
        #endregion

        #region pull all navigation hierarchy information from sharepoint and bring it into a format we can work with
        //add quick launch links in to the treeview
        public void AddQuickLaunchLinks(SPNavigationNodeCollection quicklaunchnodes)
        {
            TreeNode quickLaunchNode;
            TreeNode child;
            try
            {
                foreach (SPNavigationNode node in quicklaunchnodes)
                {
                    quickLaunchNode = new TreeNode(node.Title, "QuickLaunch", "/_layouts/images/ADDTOFAVORITES.gif", node.Url, "_self");
                    foreach (SPNavigationNode childNode in node.Children)
                    {
                        child = new TreeNode(childNode.Title, "QuickLaunch", "/_layouts/images/ADDTOFAVORITES.gif", childNode.Url, "_self");
                        quickLaunchNode.ChildNodes.Add(child);
                    }
                    treeNodes.Add(quickLaunchNode);
                }
            }
            catch (Exception) { }
        }

        //converts nodes from IHierarchyData which the tree view can't use directly in to a tree view node
        public TreeNode ConvertNode(IHierarchyData node)
        {
            try
            {
                string title = GetPropertyDescriptorValue(node, "Name");
                string url = GetPropertyDescriptorValue(node, "ServerRelativeUrl");
                //create tree node with the parameters from the originating node
                TreeNode returnedNode = new TreeNode(title, GetPropertyDescriptorValue(node, "Type"), GetPropertyDescriptorValue(node, "ImageUrl"), url, "_self");
                returnedNode.ToolTip = title;
                //returnedNode.ToolTip = GetPropertyDescriptorValue(node, "Type");
                //check for child nodes
                bool getChildren = false;
                if (node.HasChildren == true)
                {
                    getChildren = true;
                }
                SPList list = null;
                //check if the current node is a listNode
                if (returnedNode.Value == "ListNode")
                {
                    try
                    {
                        list = SPContext.Current.Web.Lists[returnedNode.Text];
                        if (list.DefaultView.Scope == SPViewScope.RecursiveAll | list.DefaultView.Scope == SPViewScope.Recursive)
                        {
                            getChildren = false;
                        }
                        list = null;
                    }
                    catch (Exception) { }
                }
                if (getChildren == true)
                {
                    //get collection
                    IHierarchicalEnumerable enumerables = node.GetChildren();
                    IHierarchyData childrenNode;

                    if (enumerables != null)
                    {
                        foreach (object dataItem in enumerables)
                        {
                            childrenNode = _dataItems.GetHierarchyData(dataItem);
                            if (childrenNode != null)
                            {
                                //recursively generate child nodes for the current node
                                returnedNode.ChildNodes.Add(ConvertNode(childrenNode));
                            }
                        }
                    }
                }
                return returnedNode;
            }
            catch (Exception ex)
            {
                //Controls.Add(new LiteralControl(ex.Message));
            }
            return null;
        }

        //Grab and convert sharepoint's built in tree view navigation so we can manipulate it
        private void InitDataSourceControl()
        {
            SPHierarchyDataSourceControl dataSourceControl = new SPHierarchyDataSourceControl();

            dataSourceControl.ShowDocLibChildren = true;
            dataSourceControl.ShowFolderChildren = true;
            dataSourceControl.ShowListChildren = true;
            dataSourceControl.ShowWebChildren = true;
            dataSourceControl.IncludeDiscussionFolders = true;
            dataSourceControl.RootContextObject = "Web";

            //export the navigation to something that is almost usable
            HierarchicalDataSourceView view = dataSourceControl.GetHierarchicalViewPublic("");
            _dataItems = view.Select();
        }

        //allows for pulling property values from hierarchy nodes so we can play with them
        private string GetPropertyDescriptorValue(IHierarchyData node, string propertyName)
        {
            PropertyDescriptorCollection propDescriptors = TypeDescriptor.GetProperties(node);

            if (propDescriptors.Count >= 1)
            {
                foreach (PropertyDescriptor property in propDescriptors)
                {
                    if (String.Compare(property.Name, propertyName, StringComparison.InvariantCultureIgnoreCase) == 0)
                    {
                        return Convert.ToString(property.GetValue(node));
                    }
                }
            }
            return null;
        }
        #endregion
    }
}
